package com.ltmonitor.jt808.service.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ltmonitor.entity.MediaItem;
import com.ltmonitor.entity.TerminalCommand;
import com.ltmonitor.entity.VehicleData;
import com.ltmonitor.jt808.model.MediaPacket;
import com.ltmonitor.jt808.protocol.JT_0200;
import com.ltmonitor.jt808.protocol.JT_0801;
import com.ltmonitor.jt808.protocol.JT_8800;
import com.ltmonitor.jt808.protocol.T808Message;
import com.ltmonitor.jt808.protocol.T808MessageHeader;
import com.ltmonitor.jt808.service.ICommandParseService;
import com.ltmonitor.jt808.service.IMediaService;
import com.ltmonitor.jt808.service.IMessageSender;
import com.ltmonitor.jt808.service.ITransferGpsService;
import com.ltmonitor.service.ILocationService;
import com.ltmonitor.service.IMediaItemService;
import com.ltmonitor.service.IRealDataService;
import com.ltmonitor.service.ITerminalCommandService;
import com.ltmonitor.service.JT808Constants;
import com.ltmonitor.util.DateUtil;
import com.ltmonitor.vo.GnssData;
import com.ltmonitor.vo.TakePhotoModel;

/**
 * 多媒体数据报解析服务
 * @author admin
 *
 */
@Service("mediaService")
public class MediaService implements IMediaService {
	private static Logger logger = Logger.getLogger(MediaService.class);

	/**
	 * 命令服务
	 */
	@Autowired
	private ICommandParseService commandService;

	/**
	 * 终端命令的持久化服务
	 */
	@Autowired
	private ITerminalCommandService terminalCommandService;
	
	/**
	 * 实时数据服务
	 */
	@Resource
	private IRealDataService realDataService;
	/**
	 * 位置服务
	 */
	@Autowired
	private ILocationService locationService;

	@Autowired
	private IMediaItemService mediaItemService;

	/**
	 * 检查分包完整性
	 */
	private Thread checkPacketThread;
	/**
	 * 媒体上传目录
	 */
	@Value("#{config.mediaUploadDir}")  
	private String mediaUploadDir;
	/**
	 * 多长时间没有收到包，视为超时
	 */
	@Value("#{config.mediaUploadTimeOut}")  
	private int mediaUploadTimeOut = 15;
	/**
	 * 检测次数，如果超过此次数，包仍然不完整，则抛弃整个包
	 */
	@Value("#{config.maxRetransTimes}")  
	private int maxRetransTimes = 3;

	/**
	 * 调用此类，对终端应答，是否需要重传，或者应答 成功
	 */
	private IMessageSender messageSender;

	/**
	 *  809转发服务
	 */
	@Autowired
	private ITransferGpsService transferGpsService;

	private boolean continueCheck = true;

	public static ConcurrentMap<String, MediaPacket> msgMap = new ConcurrentHashMap<String, MediaPacket>();

	public MediaService() {
		checkPacketThread = new Thread(new Runnable() {
			public void run() {
				checkPacketThreadFunc();
			}
		});
		checkPacketThread.start();

	}

	/**
	 *停止多媒体数据接受服务
	 */
	public void Stop() {
		continueCheck = false;
		try {
			checkPacketThread.join();
		} catch (InterruptedException e) {
			logger.error(e.getMessage(), e);
		}
	}

	/**
	 * 线程函数，不断的检测数据包是否全部收到
	 */
	private void checkPacketThreadFunc() {
		while (continueCheck) {
			Collection<MediaPacket> mpList = msgMap.values();
			for (MediaPacket mp : mpList) {
				double seconds = DateUtil.getSeconds(mp.getUpdateDate(),
						new Date());
				if (seconds > mediaUploadTimeOut) {
					if (mp.getRetransCount() > this.maxRetransTimes) {// 已经超过重传次数
						msgMap.remove(mp.getKey());
						logger.error(mp.toString() + "收包失败，超过最大重传次数:" + mp.getRetransCount());
					} else {
						// 对终端要求重传
						mp.setRetransCount(mp.getRetransCount() + 1);
						sendAck(mp);
					}
				}
				if (mp.isComplete()) {
					// 所有包都收到，从内存中移除，保存到文件中
					msgMap.remove(mp.getKey());
					saveMedia(mp);
					this.sendAck(mp);
				}
			}

			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				logger.error(e.getMessage(), e);
			}
		}
	}

	/**
	 * 收到所有包后，对终端发送应答
	 * 
	 * @param mp
	 */
	private void sendAck(MediaPacket mp) {
		ArrayList<Short> packetNos = mp.getNeedReTransPacketNo();
		JT_8800 echoData = new JT_8800();
		echoData.setMultimediaDataId(mp.getMediaId());
		echoData.setRepassPacketsCount((byte) packetNos.size());
		echoData.setRepassPacketsNo(packetNos);

		T808Message ts = new T808Message();
		ts.setMessageContents(echoData);
		ts.setHeader(new T808MessageHeader());
		ts.getHeader().setMessageType(0x8800);
		ts.getHeader().setSimId(mp.getT808Message().getSimNo());
		ts.getHeader().setIsPackage(false);

		this.messageSender.Send808Message(ts);
		if(packetNos.size() > 0)
		{
			logger.error("对终端下发多媒体重传包:" + mp.toString());
		}

	}

	/**
	 * 处理拍照上传数据
	 * 
	 * @param msg
	 */
	public void processMediaMsg(T808Message msg) {
		T808MessageHeader header = msg.getHeader();
		int packetNo = header.getMessagePacketNo();
		String key = msg.getSimNo() + "_" + msg.getMessageType() + "_"
				+ header.getMessageTotalPacketsCount();
		MediaPacket mp = msgMap.get(key);
		if (mp == null) {
			mp = new MediaPacket(msg);
			mp.setKey(key);
			msgMap.put(key, mp);
		}

		if( mp.containPacket(packetNo) == false)
			mp.addPacket(packetNo, msg.getChildPacket());

	}
	
	public final TerminalCommand getLatestCommand(String simNo) {
		try {
			String hsql = "from TerminalCommand where (cmdType = ? or cmdType= ? or cmdType= ? or cmdType = ? ) and simNo = ? and createDate > ? order by createDate desc";
			TerminalCommand tc = (TerminalCommand) terminalCommandService
					.find(hsql,
							new Object[] {
							JT808Constants.CMD_TAKE_PHOTO,
							JT808Constants.CMD_AUDIO_RECORDER,JT808Constants.CMD_MEDIA_UPLOAD_SINGLE,
							JT808Constants.CMD_MEDIA_UPLOAD,simNo,
									DateUtil.getDate(DateUtil.now(),
											Calendar.HOUR, -1) });
			return tc;
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		}
		return null;
	}
	

	/**
	 * 保存拍照文件并创建多媒体上传记录
	 * 
	 * @param mp
	 */
	@Transactional
	public void saveMedia(MediaPacket mp) {
		T808Message msg = mp.getT808Message();
		T808MessageHeader header = msg.getHeader();
		logger.error("拍照指令:" + mp.toString());
		if (msg.getMessageContents() == null) {
			logger.error(msg.getSimNo()+"0x0801拍照数据包消息体为空，无法保存照片数据!");
			return;
		}

		JT_0801 uploadMediaMsg = (JT_0801) msg.getMessageContents();
		//更新拍照指令		
		TerminalCommand tc = this
				.getLatestCommand(msg.getSimNo());

		//创建多媒体上传记录
		MediaItem mi = new MediaItem();
		if (tc != null)
			mi.setCommandId(tc.getEntityId());// 设置拍照命令Id
		mi.setCodeFormat(uploadMediaMsg.getMultidediaCodeFormat());
		mi.setMediaDataId((int) uploadMediaMsg.getMultimediaDataId());
		mi.setMediaType(uploadMediaMsg.getMultimediaType());
		mi.setChannelId(uploadMediaMsg.getChannelId());
		mi.setEventCode(uploadMediaMsg.getEventCode());
		mi.setSimNo(msg.getHeader().getSimId());

		VehicleData vd = realDataService.getVehicleData(msg.getSimNo());
		if (vd != null)
			mi.setPlateNo(vd.getPlateNo());
		else
			mi.setPlateNo(msg.getPlateNo());
		if (tc != null
				&& TerminalCommand.FROM_GOV.equals(tc.getOwner()) == true) {
			transferTo809(tc, vd, uploadMediaMsg, mp);
		}

		JT_0200 position = uploadMediaMsg.getPosition();
		try {
			if (position != null) {
				mi.setLatitude(position.getLatitude() / 1000000);
				mi.setLongitude(position.getLongitude() / 1000000);
				mi.setSpeed(position.getSpeed() / 10);
				if (locationService != null) {
					String location = locationService.getLocation(mi.getLatitude(), mi.getLongitude());
					mi.setLocation(location);
				}
				if (position.getTime() != null) {
					Date dt = DateUtil.stringToDatetime(position.getTime(),
							"yyyy-MM-dd HH:mm:ss");
					mi.setSendTime(dt); 
				}
				
			}
		} catch (Exception ex) {
			mi.setSendTime(new java.util.Date());
			logger.error("解析拍照发生异常" + ex.getMessage());
		}
		mi.setCommandType(MediaItem.UPLOAD); 

		String extention = "";
		// 多媒体文件格式
		String[] format = new String[] { "jpg", "tif", "mp3", "wav", "wmv",
				"wma" };
		String fileFormat = null;
		if (uploadMediaMsg.getMultidediaCodeFormat() > 5) {
			logger.error("非法的命令格式:" + uploadMediaMsg.getMultidediaCodeFormat());
			fileFormat = "data";
		} else
			fileFormat = format[uploadMediaMsg.getMultidediaCodeFormat()];

		if (uploadMediaMsg.getMultimediaType() == 1)
			fileFormat = "wav";// 音频
		else if (uploadMediaMsg.getMultimediaType() == 2)
			fileFormat = "wmv";// 视频
		else if (uploadMediaMsg.getMultimediaType() == 0)
			fileFormat = "jpg";// 图像

		File Directory = new File(getUploadDir());

		if (Directory.exists() == false) {
			Directory.mkdirs();
		}
		String fileName = mi.getPlateNo() + "_"
				+ DateUtil.toStringByFormat(new Date(), "yyyy-MM-dd-HH-mm-ss")
				+ "." + fileFormat;
		String fullFileName = getUploadDir() + "\\" + fileName;
		mi.setFileName(fileName);

		try {
			mediaItemService.saveOrUpdate(mi);
		} catch (RuntimeException ex) {
			logger.error(ex.getMessage(), ex);
		}

		List<byte[]> mediaData = mp.getWholePacket();
		FileOutputStream pFileStream = null;
		try {
			pFileStream = new FileOutputStream(fullFileName, true);
			//int offset = 0;
			for (byte[] b : mediaData) {
				pFileStream.write(b, 0, b.length);
				//offset += b.length;
			}
		} catch (IOException ex) {
			logger.error(ex.getMessage(), ex);
		} finally {
			if (pFileStream != null) {
				try {
					pFileStream.close();
				} catch (IOException ex) {
					logger.error(ex.getMessage(), ex);
				}
			}
		}
		if (tc != null) {
			tc.setStatus(TerminalCommand.STATUS_UPLOADED);
			terminalCommandService.saveOrUpdate(tc);
		} else {
			logger.error("找不到拍照命令:"+msg.getSimNo());
		}


	}


	/**
	 * 如果拍照命令式运管下发的，需要将拍照数据转发给运管
	 * @param tc
	 * @param vd
	 * @param uploadMediaMsg
	 */
	private void transferTo809(TerminalCommand tc, VehicleData vd,
			JT_0801 uploadMediaMsg, MediaPacket mp) {

		try {
			TakePhotoModel tp = new TakePhotoModel();
			tp.setPlateNo(vd.getPlateNo());
			tp.setPlateColor(vd.getPlateColor());
			tp.setReplayResult(1); 
			tp.setLensId(uploadMediaMsg.getChannelId());
			tp.setPhotoSizeType(1);
			tp.setPhotoFormat(uploadMediaMsg.getMultimediaType());
			GnssData g = new GnssData();
			JT_0200 p = uploadMediaMsg.getPosition();
			if (p != null) {
				g.setGpsSpeed(p.getSpeed());
				short direction = 0;
				if (p.getCourse() > 0)
					direction = p.getCourse();
				g.setDirection(direction);
				g.setAltitude(p.getAltitude());
				g.setLatitude(p.getLatitude());
				g.setLongitude(p.getLongitude());
				g.setAlarmState(p.getAlarmFlag());
				g.setRecSpeed(p.getRecorderSpeed());
				g.setPosEncrypt(0);
				g.setTotalMileage(p.getMileage());
				g.setVehicleState(p.getStatus());
				g.setPlateColor(vd.getPlateColor());
				g.setPlateNo(vd.getPlateNo());
				if (p.getTime() != null) {
					Date dt = DateUtil.stringToDatetime(p.getTime(),
							"yyyy-MM-dd HH:mm:ss");

					g.setPosTime(dt);
				}
			}
			tp.setGnssData(g);
			tp.setPhotoFormat(1);

			List<byte[]> mediaData = mp.getWholePacket();
			int totalLength = 0;
			for (byte[] b : mediaData) 
			{
				totalLength += b.length;
			}

			tp.setPhotoData(new byte[totalLength]);
			int m = 0;
			for (byte[] b : mediaData) 
			{
				System.arraycopy(b, 0, tp.getPhotoData(), m,
					b.length);		
				m+= b.length;
			}
			tp.setPhotoSize(m);
			
			this.transferGpsService.transfer(tp); 
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		}

	}


	public String getUploadDir() {
		return mediaUploadDir;
	}

	public void setUploadDir(String uploadDir) {
		this.mediaUploadDir = uploadDir;
	}


	public boolean isContinueCheck() {
		return continueCheck;
	}

	public void setContinueCheck(boolean continueCheck) {
		this.continueCheck = continueCheck;
	}

	public int getMaxRetransTimes() {
		return maxRetransTimes;
	}

	public void setMaxRetransTimes(int maxRetransTimes) {
		this.maxRetransTimes = maxRetransTimes;
	}


	public int getMediaUploadTimeOut() {
		return mediaUploadTimeOut;
	}

	public void setMediaUploadTimeOut(int mediaUploadTimeOut) {
		this.mediaUploadTimeOut = mediaUploadTimeOut;
	}

	@Override
	public void setMessageSender(IMessageSender messageSender) {
		this.messageSender = messageSender;
	}

	@Override
	public void setTransferGpsService(ITransferGpsService transferGpsService) {
		this.transferGpsService = transferGpsService;
	}



}
